Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chunked: define error for partial pulls not available #2118

Conversation

giuseppe
Copy link
Member

define a new error type so that the caller can determine whether it is safe to ignore the error and retrieve the resource fully.

Closes: #2115

the relative patch for containers/image is something like:

diff --git a/copy/single.go b/copy/single.go
index 324785a8..b7921837 100644
--- a/copy/single.go
+++ b/copy/single.go
@@ -819,8 +819,13 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to
 				logrus.Debugf("Retrieved partial blob %v", srcInfo.Digest)
 				return true, updatedBlobInfoFromUpload(srcInfo, uploadedBlob), nil
 			}
-			logrus.Debugf("Failed to retrieve partial blob: %v", err)
-			return false, types.BlobInfo{}, nil
+			// On a "partial content not available" error, ignore it and retrieve the whole layer.
+			if chunkedToc.IsPartialContentNotAvailableError(err) {
+				logrus.Debugf("Failed to retrieve partial blob: %v", err)
+				err = nil
+			}
+			return false, types.BlobInfo{}, err
+
 		}()
 		if err != nil {
 			return types.BlobInfo{}, "", err

Copy link
Contributor

openshift-ci bot commented Sep 30, 2024

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: giuseppe

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@rhatdan
Copy link
Member

rhatdan commented Sep 30, 2024

Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting from the c/image consumer, I’d prefer copy/single.go to not check for a specific c/storage error; c/image should define its own (private) error type for PutBlobPartial.

That, in turn, means that the c/storage code calling GetDiffer is going to already have a dependency on pkg/chunked; so we can put the error definition directly into pkg/chunked, and avoid the extra infrastructure to put it into chunked/internal+chunked/toc.


#2115 talks about adding a “require the GetDiffer code path to be used” option for fs-verity. We don’t need to do that right now, but how would that be added here?

It seems to me that we need a wrapping / unwrappable error mechanism (see underlying error in one of the comments), and then something like

func GetDiffer {
   …
   differ, err := getDifferInternal()
   if fallbackMarker, ok := errIsTheFallbackMarker(err); ok && fallbackDisabled {
      err = fallbackMarker.Unwrap() // no fallback, hard failure per configuration
   }
   …
}

with getDifferInternal directly, or some of its callees, adding the marker on relevant code paths.

pkg/chunked/storage_linux.go Outdated Show resolved Hide resolved
pkg/chunked/storage_linux.go Outdated Show resolved Hide resolved
pkg/chunked/storage_linux.go Outdated Show resolved Hide resolved
pkg/chunked/storage_linux.go Show resolved Hide resolved
if err != nil {
return nil, fmt.Errorf("parsing zstd:chunked TOC digest %q: %w", zstdChunkedTOCDigestString, err)
if err == nil {
return makeZstdChunkedDiffer(store, blobSize, zstdChunkedTOCDigest, annotations, iss, pullOptions)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2115 (comment) talks about the case where a registry does not support ranged requests; that should be detected somewhere in makeZstdChunkedDiffer and turned into the “fall back” error. Similarly for the other differ.

pkg/chunked/internal/errors.go Outdated Show resolved Hide resolved
pkg/chunked/storage_linux.go Show resolved Hide resolved
@cgwalters
Copy link
Contributor

Starting from the c/image consumer, I’d prefer copy/single.go to not check for a specific c/storage error; c/image should define its own (private) error type for PutBlobPartial.

One thing I don't quite understand is the circular relationship between the two projects...are we getting much out of having them be in separate git repositories even?

@mtrmac
Copy link
Collaborator

mtrmac commented Sep 30, 2024

  • There are c/image users who don’t use c/storage (registry clients, mirroring tools). In fact that’s the primary point of that concern — a fair bit of the generic c/image logic should not be hard-coding c/storage specifics.
  • The relationship is not circular (apart from one callback from c/storage back, but I can remember only that one case where we want to invert control that way)
  • The test mechanisms and CI costs are very different (sometimes in bad ways, e.g. how most of c/image tests live in the Skopeo repo); but anyway, paying the registry testing costs for c/storage, or the c/storage filesystem/driver test matrix costs for c/image, is not immediately attractive.

The relationship is not set in stone, but then again there’s a substantial cost to integrate, and no obvious benefit to doing so.

@giuseppe giuseppe force-pushed the define-error-for-unsupported-partial-pulls branch from 293985d to 4b99d02 Compare October 1, 2024 08:12
@giuseppe
Copy link
Member Author

giuseppe commented Oct 1, 2024

#2115 talks about adding a “require the GetDiffer code path to be used” option for fs-verity. We don’t need to do that right now, but how would that be added here?

The flag will probably look like fsverity=disable|auto|require. The advantage with this PR is that /pkg/chunked can finally return an error that causes the pull to fail instead of falling back to the current pull mechanism.

@giuseppe giuseppe force-pushed the define-error-for-unsupported-partial-pulls branch from 4b99d02 to f095cb5 Compare October 1, 2024 08:47
pkg/chunked/storage_linux.go Outdated Show resolved Hide resolved
pkg/chunked/storage_linux.go Show resolved Hide resolved
@giuseppe giuseppe force-pushed the define-error-for-unsupported-partial-pulls branch from f095cb5 to 8e426ac Compare October 2, 2024 08:25
pkg/chunked/storage_linux.go Show resolved Hide resolved
if err != nil {
return nil, fmt.Errorf("parsing estargz TOC digest %q: %w", estargzTOCDigestString, err)
differ, err = makeZstdChunkedDiffer(store, blobSize, zstdChunkedTOCDigest, annotations, iss, pullOptions)
if err == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this fails because the registry does not support range requests, nothing marks the failure as eligible for fallback, AFAICS.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it fail earlier when we request the TOC through a range request? So we should treat it as a failure here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICS we request the TOC inside this function, not earlier. Is that incorrect?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah sorry, yes I got confused.

I've added the following snippet:

diff --git a/pkg/chunked/storage_linux.go b/pkg/chunked/storage_linux.go
index ce8f8a2ce..26e8f1aba 100644
--- a/pkg/chunked/storage_linux.go
+++ b/pkg/chunked/storage_linux.go
@@ -196,6 +196,13 @@ func GetDiffer(ctx context.Context, store storage.Store, blobDigest digest.Diges
 	}
 
 	logrus.Debugf("Could not create differ for blob %q: %v", blobDigest, err)
+
+	// If the error is a bad request to the server, then signal to the caller that it can try a different method.  This can be done
+	// only when convert_images is disabled.
+	var badRequestErr ErrBadRequest
+	if errors.As(err, &badRequestErr) {
+		err = newErrFallbackToOrdinaryLayerDownload(err)
+	}
 	return nil, err
 }

Avoid handling cases where the server doesn't support at least 64
ranges in a request, in order to prevent falling back to the
traditional pull mechanism.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
@giuseppe giuseppe force-pushed the define-error-for-unsupported-partial-pulls branch from 8e426ac to ea95255 Compare October 2, 2024 13:51
define a new error type so that the caller can determine whether it is
safe to ignore the error and retrieve the resource fully.

Closes: containers#2115

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
@giuseppe giuseppe force-pushed the define-error-for-unsupported-partial-pulls branch from ea95255 to ed13248 Compare October 2, 2024 15:14
Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks!

@rhatdan
Copy link
Member

rhatdan commented Oct 2, 2024

/lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

pkg/chunked/GetDiffer must differentiate between failures and errors that can be ignored
4 participants